package main

import (
	"fmt"

	//导入驱动包，并执行其包的初始化方法，而无需调用
	_ "github.com/go-sql-driver/mysql"
	//数据库API操作包
	"github.com/jmoiron/sqlx"

	"sync"
)

var (
	//数据库读写锁，允许一写多读，但不允许读写同时发生
	dbMt      sync.RWMutex

	//全局唯一的mysql数据库对象指针
	mysqlDB   *sqlx.DB
)

/*
初始化mysqlDB，全局只会初始化一次
*/
func InitMysql() {
	if mysqlDB == nil {
		//连接数据库，并将连接成功的数据库对象赋值给全局数据库对象
		db, err := sqlx.Connect("mysql", "root:123456@tcp(localhost:3306)/driving_exam")
		mysqlDB = db
		HandleError(err, `sqlx.Connect("mysql", "root:123456@tcp(localhost:3306)/driving_exam")`)
	}
}

func CloseMysql() {
	if mysqlDB != nil {
		mysqlDB.Close()
	}
}

/*func ReadMysql(name string) {
	fmt.Println("ReadMysql")
	dbMt.RLock()

	InitMysql()

	results := make([]ExamResult, 0)
	err := mysqlDB.Select(&results, "select * from score where name=?;", name)
	if err != nil {
		fmt.Println("mysqlDB.Select err=", err)
		os.Exit(1)
	}
	fmt.Println(results)
	dbMt.RUnlock()

	//写入Redis
	WriteRedis(name, results[0].Score)
}*/

/*
通用的查询方法
参数：
tableName 要查询的表名
argsMap 查询条件
dest 用于接收结果的指针
返回值：
error 如果查询失败，返回错误，否则返回nil
*/
func ReadMysql(tableName string, argsMap map[string]interface{}, dest interface{}) (error) {
	fmt.Println("ReadMysql")

	//锁定读写锁的模式为只读模式，此时数据库不能被并发修改
	dbMt.RLock()

	//必要时初始化数据库对象
	InitMysql()

	/*
	根据表名和查询条件创建SQL语句
	得到拼接好的sql语句（其中值都用问号占位符表示）
	同时得到所有值的切片（[]interface{}），后续操作中填充问号占位符的值
	*/
	sql, args := CreateQuerySql(tableName, argsMap)

	//执行sql语句，将查询结果丢入dest的地址
	err := mysqlDB.Select(dest, sql, args...)

	//释放读写锁
	dbMt.RUnlock()

	/*缓存数据到Redis*/
/*	//WriteRedis(name, results[0].Score)
	name := argsMap["name"].(string)
	score := strconv.Itoa((*dest.(*[]ExamResult))[0].Score)
	cmd := "set " + name + " " + score
	//fmt.Println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",name,score,cmd)
	DoRedisCommand(cmd,"string")*/

	return err
}

/*
拼接查询的SQL语句
参数：
tableName 要查询的表名
argsMap 查询条件
返回值：
sql 拼好的SQL语句，含?占位符
args 查询条件中的值的切片，如["张三" 123]
*/
func CreateQuerySql(tableName string, argsMap map[string]interface{}) (sql string, args []interface{}) {
	selection := ""
	args = make([]interface{}, 0)
	for col, value := range argsMap {
		selection += ("and " + col + "=? ")
		args = append(args, value)
	}

	//剪切掉第一个and
	selection = selection[4 : len(selection)-1]
	selection += ";"
	sql = "select * from " + tableName + " where " + selection
	return
}

/*
拼接插入的SQL语句
参数：
tableName 要查询的表名
dataMap 要插入的数据，列表做键，列值做值
返回值：
sql 拼好的SQL语句，含?占位符
valueSlice dataMap中的值的切片，如["张三" 123]
*/
func CreateInsertSql(tableName string, dataMap map[string]interface{}) (sql string, valueSlice []interface{}) {
	//insert into xxx(col1,col2,...) valus(?,?,...);

	cols := "("
	values := "values("
	valueSlice = make([]interface{}, 0)
	for col, value := range dataMap {
		cols += (col + ",")
		values += ("?,")
		valueSlice = append(valueSlice, value)
	}

	//锯掉最后一个逗号
	cols = cols[:len(cols)-1]
	values = values[:len(values)-1]

	//拼上右括号
	cols += ")"
	values += ")"

	sql = "insert into " + tableName + cols + " " + values + ";"
	return
}

/*func WriteMysql(scoreMap map[string]int) {
	dbMt.Lock()
	fmt.Println("开始录入成绩...")

	InitMysql()

	for name, score := range scoreMap {
		_, err := mysqlDB.Exec("insert into score(name,score) values(?,?);", name, score)
		if err != nil {
			fmt.Println("插入失败 err=", err)
		} else {
			fmt.Println("插入成功！")
		}
	}

	fmt.Println("成绩录入完毕！")
	dbMt.Unlock()
}*/

/*
通用的MySQL插入方法
参数：
tableName 要查询的表名
dataMap 要插入的数据，列表做键，列值做值
*/
func WriteMysql(tableName string, dataMap map[string]interface{}) {

	//锁定为一路只写
	dbMt.Lock()
	fmt.Println("开始录入成绩...")
	/*有必要（mysqlDB还未被创建）时初始化MySQL数据库*/
	InitMysql()

	sql, valueSlice := CreateInsertSql(tableName, dataMap)
	_, err := mysqlDB.Exec(sql, valueSlice...)
	if err != nil {
		fmt.Println("插入失败 err=", err)
	} else {
		fmt.Println("插入成功！")
	}

	/*写入完毕时释放资源锁，供其它协程访问*/
	fmt.Println("成绩录入完毕！")
	dbMt.Unlock()


}
